package com.wang.jmonkey.modules.task.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.wang.jmonkey.common.model.enums.BooleanEnum;
import com.wang.jmonkey.modules.task.mapper.SystemTaskMapper;
import com.wang.jmonkey.modules.task.model.entity.SystemTaskEntity;
import com.wang.jmonkey.modules.task.model.query.SystemTaskQuery;
import com.wang.jmonkey.modules.task.service.SystemTaskService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;

/**
 * 定时任务配置表(SystemTask)表服务实现类
 *
 * @author HeJiawang
 * @since 2021-07-26 16:51:30
 */
@Slf4j
@Service
public class SystemTaskServiceImpl extends ServiceImpl<SystemTaskMapper, SystemTaskEntity> implements SystemTaskService {

    /**
     * mapper
     */
    @Autowired
    private SystemTaskMapper mapper;

    @Resource
    private Scheduler scheduler;

    /**
     * 分页查询信息
     * @param query 分页查询条件
     * @return 定时任务信息
     */
    @Override
    public PageInfo<SystemTaskEntity> page(SystemTaskQuery query) {
        Page<SystemTaskEntity> list = mapper.page(query);
        return new PageInfo<>(list);
    }

    /**
     * 校验任务类是否可用
     *  1、任务类存在
     *  2、任务类未被使用
     * @param task task
     * @return true 存在并未被使用，false不存在或已被使用
     */
    @Override
    public boolean checkClassName(SystemTaskEntity task) {
        try {
            Class.forName(task.getClassName());
        } catch (ClassNotFoundException e) {
            return false;
        }

        return mapper.checkClassName(task) <= 0;
    }

    /**
     * 新增定时任务
     * @param entity task
     * @return true 新增成功
     */
    @Override
    public boolean save(SystemTaskEntity entity) {
        return this.addJob(entity) && super.save(entity);
    }

    /**
     * 修改定时任务
     * @param entity task
     * @return true 修改成功
     */
    @Override
    public boolean updateById(SystemTaskEntity entity) {
        Boolean removeSuccess = this.removeById(entity.getId());
        if (!removeSuccess) return false;

        entity.setId(null);
        return this.save(entity);
    }

    /**
     * 删除定时任务
     * @param id task id
     * @return true 删除成功
     */
    @Override
    public boolean removeById(Serializable id) {
        try {
            SystemTaskEntity task = super.getById(id);

            JobKey key = new JobKey(task.getName(), task.getGroup());
            return scheduler.deleteJob(key) && super.removeById(task.getId());
        } catch (SchedulerException e) {

            log.error(this.getClass().getName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName(), e);
            return false;
        }
    }

    /**
     * 像任务管理器中添加任务
     * @param scheduleJob scheduleJob
     * @return true false
     */
    private boolean addJob(SystemTaskEntity scheduleJob){
        Class job ;
        try {
            job = Class.forName(scheduleJob.getClassName());
        } catch (ClassNotFoundException e) {
            log.error(this.getClass().getName(), e.getMessage());

            return false;
        }

        JobDetail jobDetail = JobBuilder.newJob(job)
                .withIdentity(scheduleJob.getName(), scheduleJob.getGroup())
                .build();
        jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);

        // 表达式调度构建器（可判断创建SimpleScheduleBuilder）
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getRule());

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(scheduleJob.getName(), scheduleJob.getGroup())
                .withSchedule(scheduleBuilder)
                .build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);

            JobKey key = new JobKey(scheduleJob.getName(), scheduleJob.getGroup());
            if(scheduleJob.getUsedStatus() == BooleanEnum.NO) scheduler.pauseJob(key);
            else scheduler.resumeJob(key);
        } catch (SchedulerException e) {
            log.error(this.getClass().getName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName(), e);

            return false;
        }

        return true;
    }

    /**
     * 暂停定时任务
     * @param task 任务信息
     * @return true 暂停成功
     */
    @Override
    public boolean pause(SystemTaskEntity task) {
        JobKey key = new JobKey(task.getName(), task.getGroup());

        try {
            scheduler.pauseJob(key);

            task.setUsedStatus(BooleanEnum.NO);
            return super.updateById(task);
        } catch (SchedulerException e) {
            log.error(this.getClass().getName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName(), e);
            return false;
        }
    }

    /**
     * 恢复定时任务
     * @param task 任务信息
     * @return true 恢复成功
     */
    @Override
    public boolean resume(SystemTaskEntity task) {
        JobKey key = new JobKey(task.getName(), task.getGroup());

        try {
            scheduler.resumeJob(key);

            task.setUsedStatus(BooleanEnum.YES);
            return super.updateById(task);
        } catch (SchedulerException e) {
            log.error(this.getClass().getName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName(), e);
            return false;
        }
    }

    /**
     * 立即执行定时任务
     * @param task 任务信息
     * @return true 任务调用成功
     */
    @Override
    public boolean startNow(SystemTaskEntity task) {
        JobKey key = new JobKey(task.getName(), task.getGroup());

        try {
            scheduler.triggerJob(key);

            return true;
        } catch (SchedulerException e) {
            log.error(this.getClass().getName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName(), e);
            return false;
        }
    }

    /**
     * 启动所有定时任务
     */
    @Override
    public void startAllTask() {
        super.list().forEach(task -> this.addJob(task));
    }
}
